﻿using System.Collections.Generic;
using UnityEngine;

namespace CWM.Skinn
{
    public static partial class SkinnEx
    {
        public static Mesh[] GetMeshIslands(Mesh source, bool compareVertices = false)
        {
            if (!source || IsNullOrEmpty(source.uv, source.vertices.Length)) return null;

            int[] uvHashes;
            if(compareVertices) uvHashes = UVIslandUtility.GetUVHashes(source.uv, source.vertices);
            //if (compareVertices) uvHashes = UVIslandUtility.GetVertexHashes(source.vertices);
            else uvHashes = UVIslandUtility.GetUVHashes(source.uv);

            var triangleHashes = UVIslandUtility.GetTriangleHashes(source.triangles);
            var trisCount = 0;
            var islands = new List<Mesh>();
            var trisKeys = new List<int>(triangleHashes.Length);
            var newMesh = source.Clone() as Mesh;
            newMesh.subMeshCount = 1;

            while (islands.Count < 1000 && trisCount < triangleHashes.Length)
            {
                var indices = new List<int>(triangleHashes.Length);
                var uvKeys = new List<int>(triangleHashes.Length);

                var count = 0;
                while (count < 30 && UVIslandUtility.AddTriangles(triangleHashes, uvHashes, ref indices, ref uvKeys, ref trisKeys)) { count++; }

                newMesh.SetTriangles(indices.ToArray(), 0);
                islands.Add(newMesh.GetSubmesh(0));
                trisCount += indices.Count;
#if CWM_DEV
            Debug.Log(string.Format("Triangle Passes: {0}, Islands {1}", count, islands.Count));
#endif

            }

            Release(newMesh);

            return islands.ToArray();
        }

        internal static class UVIslandUtility
        {
            public static int[] GetUVHashes(Vector2[] uv)
            {
                var uvHashes = new int[uv.Length];
                for (int i = 0; i < uvHashes.Length; i++) { uvHashes[i] = Animator.StringToHash((uv[i]).ToString("0.00")); }
                return uvHashes;
            }

            public static int[] GetUVHashes(Vector2[] uv, Vector3[] vertices)
            {
                var uvHashes = new int[uv.Length];
                for (int i = 0; i < uvHashes.Length; i++)
                {
                    var hashName = uv[i].ToString("0.00");
                    hashName += vertices[i].ToInches().ToString("0.000");
                    uvHashes[i] = Animator.StringToHash(hashName);
                }
                return uvHashes;
            }


            public static int[] GetVertexHashes(Vector3[] vertices)
            {
                var uvHashes = new int[vertices.Length];
                for (int i = 0; i < uvHashes.Length; i++)
                {
                    var hashName = vertices[i].ToInches().ToString("0.000");
                    uvHashes[i] = Animator.StringToHash(hashName);
                }
                return uvHashes;
            }

            public static Vector2Int [] GetTriangleHashes(int[] triangles)
            {
                var triangleHashes = new Vector2Int[triangles.Length];
                for (int ii = 0; ii < triangles.Length; ii += 3)
                {
                    int a = triangles[ii + 0], b = triangles[ii + 1], c = triangles[ii + 2];
                    var tris = Animator.StringToHash(a.ToString() + b.ToString() + c.ToString());
                    triangleHashes[ii + 0] = new Vector2Int(a, tris);
                    triangleHashes[ii + 1] = new Vector2Int(b, tris);
                    triangleHashes[ii + 2] = new Vector2Int(c, tris);
                }
                return triangleHashes;
            }

            public static bool AddTriangles(Vector2Int[] triangles, int[] uv, ref List<int> newTriangles, ref List<int> uvKeys, ref List<int> trisKeys)
            {
                int count = 0;
                for (int ii = 0; ii < triangles.Length; ii += 3)
                {
                    int a = triangles[ii + 0].x, b = triangles[ii + 1].x, c = triangles[ii + 2].x;
                    var tris = triangles[ii + 0].y;
                    if (trisKeys.Contains(tris)) continue;
                    if (newTriangles.Count < 3 || uvKeys.Contains(uv[a]) || uvKeys.Contains(uv[b]) || uvKeys.Contains(uv[c]))
                    {
                        trisKeys.Add(tris);
                        newTriangles.Add(a); newTriangles.Add(b); newTriangles.Add(c);
                        uvKeys.Add(uv[a]); uvKeys.Add(uv[b]); uvKeys.Add(uv[c]);
                        count++;
                    }
                }
                return count > 0;
            }
        }
    }
}